package com.zlm.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;

import javax.annotation.Resource;

/**
 * springsecurity的配置类
 */

@Configuration  // 继承WebSecurityConfigurerAdapter
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Resource
    private MyUserDetialService myUserDetialService;

    // 下面到了自定义数据库数据源那么下面这个就不需要了
    /*@Bean
    public UserDetailsService userDetailsService() {
        // InMemoryUserDetailsManager去实现的UserDetailsService里面的方法loadUserByUsername
        InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
        userDetailsService.createUser(User.withUsername("lemon").password("{noop}1234").roles("admin").build());

        return userDetailsService;
        // 写了这个的话下面的initialize方法就不用写了他会自动的去覆盖原来的userDetailsService
        //
    }*/

    // Springboot 对 SpringSecurity 默认配置中 在工厂中默认创建AuthenticationManager
    // 因此你可以直接注入工厂里面的authentication  因为你是用了@Autowired被spring管理了
    // 这个就是全局的
//    @Autowired
//    public void initialize(AuthenticationManagerBuilder builder) throws Exception {
//        System.out.println("Springboot 默认配置： " + builder);
//        InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
//        // 使用自己指定的
//        userDetailsService.createUser(User.withUsername("lemon").password("{noop}1234").roles("admin").build());
//        builder.userDetailsService(userDetailsService);
//    }

    /**
     * 自定义AuthenticationManager 认证的  推荐这种
     *
     * @param builder
     */
    @Override
    public void configure(AuthenticationManagerBuilder builder) throws Exception {
        //builder ....
        // 会覆盖springboot原本的
        // 他不会自动去配置，因此你需要自己手动添加进去才行
        System.out.println("自定义AuthenticationManager" + builder);
        builder.userDetailsService(myUserDetialService); // 这样就可以了 这个是修改的daoAuthentication里面的
//        builder.authenticationProvider(new DaoAuthenticationProvider())
    }


    // 你想暴露自定义生成的AuthenticationManager的话就要注入一下
    // 作用：将自定义的AuthenticationManager在工厂中进行暴露，可以在任何位置注入
    @Bean // 被Spring管理
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 进行覆盖的
     * 用来覆盖http请求的
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {


        // 默认配置则失效了
        // permitAll()代表放行匹配“index”下面的所有请求
        // authenticated() 表示需要进行认证的
        // anyRequest() 其余所有的请求都要进行认证
        // 放行资源要写在前面

        /* formLogin进行表单认证 里面就是通过usernamePasswordFilter里面的attemp()方法并且生成
         usernamepasswordAuthenticationToken来进行调用AuthenticationManager里面的authentication()方法来进行认证
         如果成功的话就返回完整的authentication否则就是报错
         */
        http.authorizeRequests()
                .mvcMatchers("/login.html").permitAll()
                .mvcMatchers("/index").permitAll()
//                .mvcMatchers("/hello").authenticated()
                .anyRequest().authenticated()
                .and().formLogin()
                .loginPage("/login.html") // 我们的登录界面 一旦自定义登录界面过后那个url处理登录请求
                .loginProcessingUrl("/doLogin") // 自定义登录的请求方法  可以在usernamepasswordFilter过滤器里面看见
                .usernameParameter("uname") // 默认从表单获取的username的key为uname 因此我们的html页面也是用这个为name
                .passwordParameter("passwd") // 默认从表单获取的password的key为passwd 因此我们的html页面也是用这个为name
                //.successForwardUrl("/index") //forward 跳转           注意:不会跳转到之前请求路径
                //.defaultSuccessUrl("/index")   //redirect 重定向    注意:如果之前请求路径,会有优先跳转之前请求路径
                .successHandler(new MyAuthenticationSuccessHandler()) // 认证成功时的处理(并且是重定向的)  前后端分离的处理方案
//                .failureForwardUrl("/login.html") // 这个是forward的 (并且地址是不会改变的 但是在login.html页面)   使用的是request
//                .failureUrl("/login.html") // 默认的 这个就是使用redirect 实现的 使用的是session
                .failureHandler(new MyAuthenticationFailureHandler()) // 自定义失败后的处理
                .and()
                .logout() // 对退出登录进行配置
//                .logoutUrl("/logout") // 配置注销的url 你直接get请求就可以了 并且默认请求方式是get方式
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/lougout1", "GET"),
                        new AntPathRequestMatcher("/bb", "POST")
                ))// 这个的话就是多个地址都可以进行登出 并且设置了请求的方式
                .invalidateHttpSession(true) // 会话失效 当前的session值失效
                .clearAuthentication(true) // 清楚当前认证信息
//                .logoutSuccessUrl("/login.html") // 登出成功后的跳转页面
                .logoutSuccessHandler(new MyLogoutSuccessHandler()) // 前后端分离的写法
                .and()
                .csrf().disable(); //这里先关闭 CSRF
        ;
        // and的意思就是连接一下
        // http.formLogin()是一个用法的

        // 父类的话还是去执行所有请求必须认证
//        super.configure(http);
    }
}
